<template>
  <div style="width:100%;height:100%;position:relative;z-index:1;">
    <div :id="videoContainer" ref="videoContainer" class="video-container">
      <video
        width="100%"
        height="100%"
        :id="playerCont"
        preload="auto"
        playsinline
        webkit-playsinline
        autoplay
      ></video>
    </div>
    <template>
      <div v-show="isPaused" class="pause-bg" @click.stop="playVideo">
        <img
          class="puase-img"
          src="//image.zlketang.com/public/news/images/new_official/play/play_play_icon.png"
          alt=""
        />
      </div>
      <div v-show="isShowErrorDialog" class="error-dialog">
        <div class="flex-center flex-column-wrap" style="text-align: center">
          <span>网络错误导致视频下载中途失败</span>
          <div class="flex-wrap" style="margin-top: 20px">
            <div class="error-btn flex-wrap flex-center" @click="relaodVideo">
              <zl-icon
                icon="icon-shuaxin1"
                :icon-style="{ width: '30px', height: '30px' }"
              ></zl-icon>
              <div style="margin-left: 5px">刷新重试</div>
            </div>
            <div
              v-if="playingSection.domains && playingSection.domains.length"
              class="error-btn flex-wrap flex-center"
              style="margin-left: 20px"
              @click="toggleLine"
            >
              <zl-icon
                icon="icon-qiehuanluxian"
                :icon-style="{ width: '30px', height: '30px' }"
              ></zl-icon>
              <div style="margin-left: 5px">切换线路</div>
            </div>
          </div>
        </div>
      </div>
      <div v-show="isShowLoding" class="loading-box flex-center">
        <div class="loading flex-center flex-column-wrap">
          <img
            class="loading-gif"
            src="//image.zlketang.com/public/news/images/new_official/play/play_loading.gif"
            alt=""
          />
          <span>加载中</span>
        </div>
      </div>
      <div class="control-tips-box" v-show="isShowVolume">
        <div>当前音量:{{ volume }}</div>
      </div>
      <div class="watch-tips-box" v-show="isShowTrySee || isShowActiveCourse">
        <div v-show="isShowTrySee">
          试看5分钟,
          <a
            class="tips-a"
            style="color:#80B2F3"
            href="javascript:;"
            @click="showLogin"
          >
            登录
          </a>
          免费领取完整课程
        </div>
        <div v-show="isShowActiveCourse">
          点击
          <a class="tips-a" @click="goToNewUserActivity">激活完整课程</a>
        </div>
      </div>
    </template>
    <!-- 植入广告 -->
    <div
      v-show="isShowAdverts"
      class="adverts-container"
      style="overflow: hidden;"
    >
      <div
        id="advertsContainer"
        style="
          position: relative;
          overflow: hidden;  
          width: 303px;
          height: 266px;
        "
      >
        <div style="position: absolute;left: 400px;" class="animate-box">
          <div class="adverts-time-box flex-center">
            <div>{{ showAdvertsCountdown }}s后自动关闭</div>
            <div class="adverts-time-box-line">|</div>
            <div
              @click="closeAdversDialog"
              style="cursor: pointer;"
              class="iconfont icon-guanbi"
            ></div>
          </div>
          <div @click="goToAdvert" class="adverts-card pt6">
            <img
              class="adverts-card-img"
              :src="parseResourceUrl(advertisingObj.cover_url)"
            />
            <div class="adverts-card-font">
              {{ advertisingObj.name }}
            </div>
            <div class="adverts-card-btn">
              <div>
                {{ advertisingObj.btn_name }}
              </div>
              <div class="iconfont icon-quanbu adverts-card-btn-icon"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import entryStore from '@/store/entry_store'
import { getTcVideo, getAwsVideo } from '@/api/play'
import zlIcon from '@/components/zlIcon.vue'
export default {
  name: 'palyPlayer',
  components: {
    'zl-icon': zlIcon
  },
  props: {
    isInitPlayer: {
      type: Boolean,
      default: true
    },
    'episodic-type': {
      type: Number,
      default: 1
    },
    'page-type': {
      type: String,
      default: 'common'
    },
    /*
      视频播放信息：
      （1）src为字符串时，格式为：“//image.zlketang.com/public/news/videos/taxman_double_five.mp4”，为视频资源播放地址；
      （2）src为对象时，格式为{fileID: ''}，为腾讯专属播放格式
      */
    playOptions: {
      type: Object,
      default: () => {}
    },
    // 下次播放的时候从这个时间开始
    currentTimePlay: {
      type: Number,
      default: 0
    },
    // 当前播放时间
    playNowTime: {
      type: Number,
      default: 0
    },
    //是否自动播放，默认否
    autoplay: {
      type: Boolean,
      default: true
    },
    // 是否默认静音
    muted: {
      type: Boolean,
      default: false
    },
    // 当前播放速度
    currentRate: {
      type: [Number, String],
      default: 1
    },
    videoContainer: {
      type: String,
      default: 'zl_video_container'
    },
    playerCont: {
      type: String,
      default: 'playerCont'
    },
    isPlayerNew: {
      type: Boolean,
      default: false
    },
    hasAdverts: {
      type: Boolean,
      default: false
    },
    // 课程信息
    courseInfo: {
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
      // 是否为组件初始化
      // isInit: true,
      //当前的视频播放器实例
      player: null,
      // 重试定时器
      retryTimer: null,
      //重连计数
      retryCount: 0,
      // 初始化参数
      initParams: {},
      //是否暂停视频
      isPaused: false,
      // 上下左右keycode码，
      LEFT_KEY_CODE: 37,
      RIGHT_KEY_CODE: 39,
      BOTTOM_KEY_CODE: 40,
      TOP_KEY_CODE: 38,
      SPACE_KEY_CODE: 32,
      // 快进快退的时间间隔
      fastForwardTime: 5,
      // 上下键控制音量间隔
      volumeStep: 5,
      volume: 50,
      // 展示音量调整
      isShowVolume: false,
      isShowLoding: true,
      // 用于再播放时监听视频因网络问题导致的暂停
      currentOldTime: '',
      currentNewTime: '',
      netSlowTimer: null,
      // 默认选中的线路
      lineIndex: 0,
      // 是否显示错误弹窗
      isShowErrorDialog: false,
      recordedBroadcastList: [],
      hasPreSection: false, //是否有上一节的按钮
      // 初始化字幕样式
      initSubtitleStyle: {
        backgroundColor: '#000',
        backgroundOpacity: '0',
        color: '#FFF',
        edgeStyle: 'dropshadow',
        fontFamily: 'proportionalSansSerif',
        fontPercent: 1, // 需要是数字类型才生效
        textOpacity: '0',
        windowColor: '#000',
        windowOpacity: '0'
      },
      // 当前字幕样式
      currentSubtitleStyle: {},
      advertisingObj: {}, //当前展示的广告对象
      sectionAdvertsList: [], //当前课节全部广告信息数组
      isShowAdverts: false, //是否展示广告
      showAdvertsCountdown: 8, //广告倒计时的秒数
      shakeAnimate: true,
      zlaBtn: {
        key_name: '',
        key_content: ''
      },
      jumpTypeMap: {
        1: '跳转商品',
        2: '跳转直播',
        3: '广告地址'
      }
    }
  },
  computed: {
    // 获取当前用户信息
    currentUser() {
      try {
        return JSON.parse(storage.get('currentUserInfo')) || {}
      } catch (error) {
        return {}
      }
    },
    isShowTrySee() {
      return (
        this.$store.state.play &&
        this.$store.state.play.isShowVideoStatus === 'trySee'
      )
    },
    isShowActiveCourse() {
      return (
        this.$store.state.play &&
        this.$store.state.play.isShowVideoStatus === 'activeCourse'
      )
    },
    // 正在播放的视频
    playingSection() {
      return (
        (this.$store.state.play && this.$store.state.play.playingSection) || {}
      )
    },
    sectionDetal() {
      return this.$store.state.play.sectionDetal || {}
    },
    courseMkInfo() {
      //目录信息
      return this.$store.state.play.courseMkInfo
    },
    // 是safari浏览器打开
    isSafariOpen() {
      if (typeof window === 'undefined') return
      //Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15
      let ua = navigator.userAgent
      let reg = /Version\/\d+/
      return ua.includes('Mac OS') && reg.test(ua) && !ua.includes('Chrome')
    }
  },
  methods: {
    // 修改播放配置
    changePlayConfig(row) {
      let localConfig = storage.get('videoConfig') || '{}'
      try {
        localConfig = JSON.parse(localConfig)
      } catch (e) {
        localConfig = {}
      }

      localConfig = {
        ...localConfig,
        ...row
      }
      storage.set('videoConfig', JSON.stringify(localConfig))
    },
    // 前进后退
    setForwardBackward() {
      // 获取控制按钮的父元素
      let controlBox = document.querySelector('.vjs-custom-control-spacer')
      let f_el = document.querySelector('.forward-box-img')
      let b_el = document.querySelector('.backward-box-img')
      if (f_el || b_el || !controlBox) return
      // 创建样式元素
      f_el = document.createElement('img')
      f_el.src =
        '//image.zlketang.com/public/news/others/imgs/web_pc/5510ac8bad62f39b6675a12574347598.png'
      f_el.classList.add('forward-box-img')
      b_el = document.createElement('img')
      b_el.src =
        '//image.zlketang.com/public/news/others/imgs/web_pc/0283cad753b8be5df7a764d78f66dd31.png'
      b_el.classList.add('backward-box-img')
      controlBox.appendChild(b_el)
      controlBox.appendChild(f_el)

      // 注入点击事件-前进15s
      f_el.addEventListener('click', () => {
        let videoDom = document.querySelector('video')
        videoDom.currentTime += 15
      })
      // 注入点击事件-后退15s
      b_el.addEventListener('click', () => {
        let videoDom = document.querySelector('video')
        videoDom.currentTime -= 15
      })
    },
    // 注入设置
    setSys() {
      let el = document.querySelector('.vjs-sys')
      if (el) return
      // 创建样式元素
      let str = `<div class="vjs-sys">设置
         <div class="vjs-sys-hover-box">
         <div class="flex-center-wrap">
          <div class="vjs-sys-label">播放方式：</div>
          <div class="flex-center-wrap">
                <div class="vjs-sys-span m-r-20" id="play-stop-span">播完暂停</div>
                <div class="vjs-sys-span" id="play-auto-span">自动连播</div>
          </div>
          </div>
          </div>
         </div>
      </div>`
      // 在截图前插入节点
      let rateBox = document.querySelector('.screen-shot-line-btn')

      let controlBox = document.querySelector('.vjs-control-bar')
      if (!rateBox || !controlBox) return
      controlBox.insertBefore($(str)[0], rateBox)
      // 注入相关事件
      let el_content = $('.vjs-sys')
      let el_stop = $('#play-stop-span')
      let el_auto = $('#play-auto-span')
      let el_switch = $('#switch-box')
      if (!el_stop || !el_auto || !el_switch || !el_content) return
      el_content.on('click', event => {
        // 阻止事件冒泡
        event.stopPropagation()
      })

      // 相关节点的默认状态设置
      let { autoplay, episodicType } = this

      // 绑定点击事件
      el_stop.on('click', () => {
        el_stop.addClass('vjs-sys-span-active')
        el_auto.removeClass('vjs-sys-span-active')
        this.changePlayConfig({
          episodicType: 1
        })
        this.$emit('changeEpisodicType', 1)
      })
      el_auto.on('click', () => {
        el_auto.addClass('vjs-sys-span-active')
        el_stop.removeClass('vjs-sys-span-active')
        this.changePlayConfig({
          episodicType: 2
        })
        this.$emit('changeEpisodicType', 2)
      })

      el_switch.on('click', () => {
        el_switch.toggleClass('vjs-switch-active')
        this.changePlayConfig({
          isAutoPlay: el_switch.hasClass('vjs-switch-active') ? 1 : 2
        })
        this.$emit('changeAutoPlay', !autoplay)
      })

      if (autoplay) {
        el_switch.addClass('vjs-switch-active')
      } else {
        el_switch.removeClass('vjs-switch-active')
      }

      // episodicType 1 播放暂停 2 连续播放(默认)
      if (episodicType === 1) {
        el_stop.addClass('vjs-sys-span-active')
        el_auto.removeClass('vjs-sys-span-active')
      } else {
        el_auto.addClass('vjs-sys-span-active')
        el_stop.removeClass('vjs-sys-span-active')
      }
    },
    initial() {
      //拦截tcplayer的部分报错
      if (window.videoRuntime) {
        return
      }
      //设置播放器全局变量，只要关注首次播放与倍速
      window.videoRuntime = {
        playbackRate: 1, //当前页面全局记录生产生命周期
        lastUpdateTime: 0, //存入window对象下面保存，避免组件销毁时去掉进度
        video_id: this.playOptions.video_id, // 记录当前播放的video_id，当切换视频时需要需要清楚lastUpdateTime
        volume: this.volume / 100,
        lineIndex: 0 //播放线路 index 每次切换视频都置为0
      }
    },
    // 点击登录 弹出登录弹窗
    showLogin() {
      this.pauseVideo()
      this.showLoginDialog()
    },
    goToNewUserActivity() {
      storage.set('isGiftClick', 1)
      this.$store.commit('user/setIsShowGift', true)
    },
    //动态加载腾讯webPlayer 资源文件
    async preparePlayer() {
      let { autoplay } = this
      //水印为当前用户id+时间戳(小时) 当用户删除本地用户记录信息，则用cookie作为用户水印
      let userInfo = null
      try {
        await this.getUserNum().then(res => {
          userInfo = res
        })
      } catch (error) {}
      let userMark = userInfo ? userInfo.user_num : ''

      userMark += parseInt(Date.now() / 3600000)
      // 初始化并添加水印
      let watermarkConfig = {
        type: 'text',
        content: userMark, // 水印内容
        speed: '', //取值范围 0-1
        opacity: 0.15,
        color: '#E4EAE7',
        fontSize: '22px'
      }
      let isAutoPlay = !this.isInitPlayer || autoplay
      this.initParams = {
        plugins: {
          // ios因为video全屏会被劫持的原因 故不添加水印
          DynamicWatermark: watermarkConfig
        },
        // muted: this.isInitPlayer,
        autoplay: isAutoPlay, // 是否自动播放
        posterImage: false,
        playbackRates: [0.8, 1, 1.25, 1.5, 1.75, 2], //设置变速播放倍率选项
        controlBar: {
          // children: ['subsCapsButton','playbackRateMenuButton', 'fullscreenToggle'],
          subsCapsButton: false, // 去除原生字幕按钮
          textTrackSettings: false
        },
        persistTextTrackSettings: true // 设置字幕配置写入loacalstorage
      }
      this.$emit('changeIsInitPlayer', false)
      if (this.playOptions.hls) {
        // vod 域名没有 HTTP证书 需要切换为cdn播放
        this.initParams.src = this.playOptions.hls.replace(
          '//vod.zlketang.com',
          '//cdn.zlketang.com'
        )
        this.initParams.src = this.initParams.src.replace('http://', 'https://')
      } else {
        this.initParams.fileID = this.playOptions.txvid
        this.initParams.psign = this.playOptions.play_auth
          ? this.playOptions.play_auth.p_sign
          : null
        this.initParams.appID = this.playOptions.play_auth
          ? this.playOptions.play_auth.app_id
          : null
      }
      // 当传了customHost  则表示使用腾讯云的另一域名拉取资源
      if (this.playOptions.customHost) {
        this.initParams.psign = this.playOptions.p_sign
      }
      this.initPlayer()
    },
    // 返回值是promise
    async getUserNum() {
      let cacheKey = '___zhiliao_user_info___'
      let pageRecorder = this.getCookie('PAGERECORDER')
      let uid = ''
      if (pageRecorder) {
        try {
          uid = JSON.parse(window.atob(unescape(pageRecorder))).uid
        } catch (e) {}
      }
      // 【1】缓存中的用户信息存在user_num并且uid匹配，则用缓存
      try {
        if (uid) {
          let userInfo = storage.get(cacheKey)
          userInfo = userInfo ? JSON.parse(userInfo) : null
          if (userInfo && userInfo.uid === uid && userInfo.user_num)
            return userInfo
        }
      } catch (error) {}
      // 【2】调用接口数据
      await this.$store.dispatch('user/getUserInfo')
      let { openid, user_num } = this.$store.state.user.userData
      let userInfo = { openid, user_num, uid }
      storage.set(cacheKey, JSON.stringify(userInfo))
      return userInfo
    },
    // 初始化tcPlayer
    initPlayer() {
      let copyPlayParams = this.cloneDeep(this.initParams)
      if (
        !copyPlayParams.src &&
        !copyPlayParams.fileID &&
        !location.href.includes('free_course_play.html')
      ) {
        this.$message({
          type: 'warning',
          message: '初始化失败，无效的播放参数~'
        })
        return
      }
      let videoContainer = document.querySelector(`#${this.videoContainer}`)
      if (!videoContainer) return
      // 重新创建video标签
      if (!document.querySelector('video')) {
        let html = `<video width="100%" height="100%" id="${this.playerCont}" preload="auto" playsinline webkit-playsinline autoplay ></video>`
        videoContainer.innerHTML = html
      }
      // 未加密视频
      if (!copyPlayParams.fileID) {
        // 播放链接方式
        this.player = TCPlayer(this.playerCont, copyPlayParams)
        this.player.src(copyPlayParams.src)
      } else {
        // 播放fileId方式
        this.player = TCPlayer(this.playerCont, copyPlayParams)
        //屏蔽在fileID情况下，拉取腾讯云默认字幕
        if (this.player.mediaAsyncLoader) {
          this.player.mediaAsyncLoader.setVttThumbnail = function(e) {}
          this.player.mediaAsyncLoader.setVttThumbnailV4 = function(e) {}
        }
      }
      // 直接修改 倍速 对于未加密的视频不能够继承倍速，所以统一加入延迟执行。
      setTimeout(() => {
        this.player.playbackRate(window.videoRuntime.playbackRate)
      }, 0)
      // 设置自动播放
      this.playVideo()
      // 暴露钩子方法
      this.emitPlayerHooks()
      // 设置自定义内容,不能放置钩子以内
      this.setCustomSettings()
      // 判断是否为同一视频播放，不是则清除上次video_id的lastUpdateTime 记录
      if (
        !this.playOptions.video_id ||
        window.videoRuntime.video_id !== this.playOptions.video_id
      ) {
        window.videoRuntime.lastUpdateTime = 0
        window.videoRuntime.video_id = this.playOptions.video_id
      }
    },
    // 执行播放
    playVideo() {
      this.player.play()
    },
    pauseVideo() {
      this.player.pause()
    },
    // 改变全屏状态
    changeFullScreen() {
      // TODO isFullscreen为什么无法监控esc退出全屏
      // if(!this.player.isFullscreen()){
      this.$parent.exitVideoFullScreen()
      // }
      // 目前只有看课页和免费看课页配置了属性hasAdverts 才有植入广告功能
      if (this.hasAdverts) {
        // 插入广告相关
        this.insertAdvertsFullScreen()
      }
      this.pageBlur()
    },
    //让页面失焦，焦点回到BODY上，让上下左右键能够顺利调节音量和进度
    pageBlur() {
      document.activeElement.blur()
    },
    // 暴露钩子方法
    emitPlayerHooks() {
      // 当视频解析完成之后执行，已经能够获取播放信息
      this.player.on('loadedmetadata', () => {
        this.setCurrentTime('loadedmetadata')
        this.handleNetSlow()
        //设置进度条打点
        this.setProgressMark()
        this.isPaused = true
        this.$emit('duration', this.player.duration())
        // 需要使用方法设置倍速 videojs中有playbackRate方法，腾讯云sdk中没有写
        //放到 obj.load() 前不生效，只有放到 obj.load() 后才产生效果，请注意
        this.player.playbackRate(window.videoRuntime.playbackRate)
        // 继承音量
        if (window.videoRuntime.volume !== undefined) {
          let videoDom = document.querySelector('video')
          videoDom.volume = window.videoRuntime.volume
          this.volume = window.videoRuntime.volume * 100
        }
        // 处理 名师推荐(广告)去重 的展示列表
        // 目前只有看課頁和免費看課頁植入了廣告
        if (this.hasAdverts) {
          this.$parent.sortAdverts()
        }
      })
      this.player.on('play', () => {
        this.$emit('play')
      })
      this.player.on('playing', () => {
        this.isPaused = false
        this.$emit('playing', this.player && this.player.currentTime())
      })
      this.player.on('timeupdate', e => {
        let currentTime = this.player && this.player.currentTime()
        // 因为加载失败时也会触发timeupdate,当真正播放时 currentTime 是大于0的
        // 记录上次播放时间也需要在播放时进行
        //retryCount需要放在进度更新里面，不然会出现在无网络的时候一直retry
        if (currentTime > 0) {
          this.retryCount = 0
          window.videoRuntime.lastUpdateTime = currentTime
        }
        this.$emit('timeupdate', currentTime, false)
        // 目前只有看課頁和免費看課頁植入了廣告
        if (this.hasAdverts) {
          this.configureVideoInsertionAdvertisement(currentTime)
        }
      })

      this.player.on('seeked', e => {
        let currentTime = this.player && this.player.currentTime()
        this.$emit('timeupdate', currentTime, true)
      })

      this.player.on('pause', () => {
        this.isPaused = true
        this.$emit('pause')
      })

      this.player.on('ended', () => {
        if (this.hasAdverts) {
          this.$parent.isShowAdertsList = false
        }
        let currentTime = this.player && this.player.currentTime()
        this.$emit('ended', currentTime)
      })
      this.player.on('ratechange', e => {
        let video = document.querySelector('video')
        if (video) {
          this.$emit('rateRange', video.playbackRate)
          window.videoRuntime.playbackRate = video.playbackRate
        }
        //让焦点回到BODY，保证上下左右键控制音量、进度生效
        this.pageBlur()
      })
      this.player.on('error', e => {
        this.isShowLoding = true
        if (this.retryTimer) {
          clearTimeout(this.retryTimer)
          this.retryTimer = null
        }
        this.retryTimer = setTimeout(() => {
          this.retryCount++
          if (this.retryCount > 2) {
            this.isShowLoding = false
            this.isShowErrorDialog = true
            this.$emit('error')
            // 重试3次
            this.retryCount = 0
            return false
          }
          if (this.player) {
            try {
              this.player.dispose() //重置video
            } catch (error) {
              this.player = false
            }
          }
          this.initPlayer()
        }, 1 * 1000)
      })
      // 监听音量改变
      this.player.on('volumechange', () => {
        let video = document.querySelector('video')
        if (video) {
          window.videoRuntime.volume = video.volume
          this.volume = parseInt(video.volume * 100)
        }
      })

      //监控视频是否点击全屏或者退出全屏
      this.player.on('fullscreenchange', () => {
        this.changeFullScreen()
        //
        this.$emit('fullScreenChange')
      })
    },
    //secs：传入秒数
    setCurrentTime(type) {
      let currentTime =
        window.videoRuntime.lastUpdateTime || this.currentTimePlay
      if (this.isPlayerNew && storage.get('last_site_play_time')) {
        currentTime = +storage.get('last_site_play_time')
        storage.remove('last_site_play_time')
      }
      this.player && this.player.currentTime(currentTime)
    },
    //播放对应秒数
    seekTime(second) {
      this.player && this.player.currentTime(second)
    },
    // 设置自定义内容
    setCustomSettings() {
      if (this.isPlayerNew) {
        //知识点切换
        this.setSiteBtn()
        // 截图
        this.screenShot()
        // 课程选择
        this.openSubjectChoseDialog()
        // 上一节
        this.preSection()
        // 下一节
        this.nextSection()
        // 调整控制按钮的顺序
        this.sortVideoBtn()
      }
      // 设置字幕
      this.setSubtitle()
      // 线路切换
      this.setLineSwitch()
      //自定义倍速点击事件
      this.customRateClick()

      // 设置
      this.setSys()
      // 前进后退
      this.setForwardBackward()
    },
    // 添加自定义dom按钮
    appendCustomButton(options) {
      return new Promise(resolve => {
        let { title, innerHTML, initDomFunc, callback, elName } = options
        let myButton = this.player.controlBar.addChild('button')
        myButton.controlText(title)
        this.player.controlBar
          .el()
          .insertBefore(
            myButton.el(),
            this.player.controlBar.getChild(elName).el()
          )
        let buttonDom = myButton.el()
        buttonDom.innerHTML = innerHTML
        initDomFunc && initDomFunc()
        // 只能绑定一次事件
        buttonDom.addEventListener(
          'mouseover',
          function() {
            callback && callback()
          },
          { once: true }
        )
        resolve()
      })
    },
    // 添加字幕自定义按钮
    setSubtitleButton() {
      let options = {
        title: '字幕',
        innerHTML: `<div class="vjs-custom-box">
                      <span class="vjs-custom-button">字幕</span>
                      <div class="vjs-custom-content">
                        <div class="subtitle-setting flex-column-wrap flex-justify-center">
                          <div class="flex-center-wrap subtitle-switch">
                            <div>字幕开关：</div>
                            <input class="subtitle-checkbox" type="checkbox"></input>
                          </div>
                          ${
                            !this.isSafariOpen
                              ? `     
                            <div class="subtitle-size-wrap">
                              <div>字幕大小：</div>
                              <div class="flex-center-wrap flex-justify-between subtitle-size-box">
                                <span data-size="0.75" class="subtitle-size">小</span>
                                <span data-size="1" class="subtitle-size">中</span>
                                <span data-size="1.25" class="subtitle-size">大</span>
                              </div>
                            </div>`
                              : ''
                          }
                        </div>
                      </div>
                    </div>
                  `,
        // 赋值初始化
        initDomFunc: this.initSubtitle,
        // 回调方法
        callback: this.subTitleControl,
        elName: 'fullscreenToggle'
      }
      this.appendCustomButton(options)
    },
    /**添加字幕start */
    initSubtitle() {
      let vjsSettings = storage.get('vjs-text-track-settings')
      vjsSettings = vjsSettings
        ? JSON.parse(vjsSettings)
        : this.initSubtitleStyle
      let { fontPercent } = vjsSettings
      let switchDom = document.querySelector('.subtitle-checkbox')
      let isShowSubtitle = storage.get('isShowSubtitle')
      switchDom.checked =
        typeof isShowSubtitle !== 'string' || +isShowSubtitle === 1
      Array.from(document.querySelectorAll('.subtitle-size')).forEach(item => {
        let _size = item.getAttribute('data-size')
        if ((!fontPercent && +_size === 1) || +fontPercent === +_size) {
          item.className = 'subtitle-size subtitle-size-active'
        } else {
          item.className = 'subtitle-size'
        }
      })
    },
    subTitleControl() {
      let switchDom = document.querySelector('.subtitle-checkbox')
      if (!switchDom) return
      switchDom.addEventListener('click', e => {
        e.stopPropagation()
        let isChecked = e.target.checked
        // 显示隐藏字幕
        this.toggleTacksShow(isChecked)
        // 是否显示字幕 0 不显示 1 显示
        storage.set('isShowSubtitle', +isChecked)
      })
      let sizeBoxDom = document.querySelector('.subtitle-size-box')
      if (!sizeBoxDom) return
      sizeBoxDom.addEventListener('click', e => {
        e.stopPropagation()
        let size = e.target.getAttribute('data-size')
        if (typeof size !== 'string') return
        Array.from(document.querySelectorAll('.subtitle-size')).forEach(
          item => {
            let _size = item.getAttribute('data-size')
            if (+size === +_size) {
              item.className = 'subtitle-size subtitle-size-active'
            } else {
              item.className = 'subtitle-size'
            }
            this.currentSubtitleStyle.fontPercent = +size
          }
        )
        this.updateSubtitleStyle()
      })
    },
    updateSubtitleStyle() {
      var settings = this.player.textTrackSettings
      // 设置字幕样式的字段属性固定，值选项也固定，否则设置不生效
      // 当字体不缩放时，需要重置样式后再设置样式，否则字体大小始终是上次的大小样式
      if (this.currentSubtitleStyle.fontPercent === 1) {
        settings.setDefaults()
      }
      settings.setValues(this.currentSubtitleStyle)
      settings.updateDisplay()
      storage.set(
        'vjs-text-track-settings',
        JSON.stringify(this.currentSubtitleStyle)
      )
    },
    //设置字幕
    setSubtitle() {
      let { subtitle_version, video_id } = this.playOptions
      // subtitle_version='http://tcplayer-1306264703.cos.ap-nanjing.myqcloud.com/vtt/%E4%B8%AD%E6%96%87.vtt'
      if (!subtitle_version) return
      let vttUrl = ''
      // 完整的字幕地址直接使用
      if (subtitle_version.includes('http')) {
        vttUrl = subtitle_version
      } else {
        vttUrl = `/project/subtitle/${video_id % 1000}/${video_id}.vtt`
        if (+subtitle_version > 1) {
          vttUrl += `?v=${subtitle_version}`
        }
        vttUrl = this.parseResourceUrl(vttUrl, -1)
      }
      if (!vttUrl) return
      // 添加自定义字幕按钮
      this.setSubtitleButton()
      // 字幕加载需要放置ready时才生效
      this.player.on('ready', () => {
        this.currentSubtitleStyle = this.initSubtitleStyle
        var subTrack = this.player.addRemoteTextTrack(
          {
            src: vttUrl,
            kind: 'subtitles',
            srclang: 'zh-cn',
            label: '中文',
            default: true
          },
          true
        )
        //手动设置后会 如果persistTextTrackSettings字段设置为true，则在localstorage中，写入vjs-text-track-settings 设置，后面字幕样式以内存中的设置为准
        //未设置persistTextTrackSettings则每次刷新使用setValues中配置
        // 设置字幕样式  设置字幕样式的字段属性固定，值选项也固定，否则设置不生效
        var settings = this.player.textTrackSettings
        settings.setValues(this.currentSubtitleStyle)
        subTrack.addEventListener('load', () => {
          // 取字幕是否显示初始值
          let isShowSubtitle = storage.get('isShowSubtitle') || 1
          this.toggleTacksShow(+isShowSubtitle === 1)
        })
      })
    },
    //控制显示字幕
    toggleTacksShow(isChecked) {
      if (!this.player.textTracks() || !this.player.textTracks().tracks_) return
      let tracks = this.player.textTracks().tracks_
      let trackDom = document.querySelector('track')
      tracks.forEach((track, index) => {
        //必须是之了课堂的字幕文件
        if (!track.src || track.src.includes('/project/subtitle/')) {
          track.mode = isChecked ? 'showing' : 'disabled'
          // 兼容ios低版本
          if (trackDom && this.isSafariOpen)
            trackDom.setAttribute('kind', isChecked ? 'subtitles' : '')
        } else {
          track.mode = 'disabled'
        }
      })
    },
    /**添加字幕over */
    /**线路切换start */
    setLineSwitch() {
      // 设置线路
      this.lineIndex = window.videoRuntime ? window.videoRuntime.lineIndex : 0
      let { domains } = this.playingSection
      if (!Array.isArray(domains) || !domains.length) return
      let _domains = JSON.parse(JSON.stringify(domains))
      _domains.unshift({ t: 0 })
      this.lineIndex = this.lineIndex > _domains.length - 1 ? 0 : this.lineIndex
      let liList = ''
      _domains.forEach((item, index) => {
        liList += `<li class="${
          +this.lineIndex === +index
            ? 'toggle-line-li active-line'
            : 'toggle-line-li'
        }" data='${JSON.stringify(item)}' index="${index}">线路${index +
          1}</li>`
      })
      let options = {
        title: '线路',
        innerHTML: `<div class="vjs-custom-box">
                      <span class="vjs-custom-button">线路</span>
                      <div class="vjs-custom-content">
                        <ul class="toggle-line-list">
                         ${liList}
                        </ul>
                      </div>
                    </div>
                  `,
        // 赋值初始化
        // initDomFunc: this.initLineSwitch,
        // 回调方法
        callback: this.lineSwitchControl,
        elName: 'playbackRateMenuButton'
      }
      this.appendCustomButton(options)
    },
    lineSwitchControl() {
      let ulDom = document.querySelector('.toggle-line-list')
      if (!ulDom) return
      ulDom.addEventListener('click', e => {
        let eventDom = e.target
        let index = eventDom.getAttribute('index')
        if (+index === +this.lineIndex) return
        if (eventDom.tagName !== 'LI') return
        this.lineIndex = +index
        window.videoRuntime.lineIndex = this.lineIndex
        let lineData = JSON.parse(eventDom.getAttribute('data'))
        this.changeLine(lineData, index)
      })
    },
    // 切换线路操作
    changeLine(lineData, index) {
      try {
        Array.from(document.querySelectorAll('.toggle-line-li')).forEach(
          item => {
            let _index = item.getAttribute('index')
            if (+index === +_index) {
              item.className = 'toggle-line-li active-line'
            } else {
              item.className = 'toggle-line-li'
            }
          }
        )
      } catch (error) {}
      switch (+lineData.t) {
        case 0:
        case 1:
          this.toggleDomain(lineData)
          break
        case 2:
          this.toggleTcLine(index)
          break
        case 3:
          this.toggleAliAes(index)
          break
        case 4:
          this.toggleTcEncryptLine(lineData)
          break
        default:
          this.toggleDomain()
          break
      }
    },
    // 切换线路
    toggleDomain(data) {
      const { hls } = this.sectionDetal
      let videoUrl = '' //视频播放地址
      // 如果data.d存在，说明是切换线路3，则进行域名替换操作
      if (data.d) {
        const arr = hls.split('/')
        arr[2] = data.d
        videoUrl = arr.join('/')
      } else {
        // data.d不存在，说明是切换至默认线路，直接用hls即可
        videoUrl = hls
      }

      this.$store.commit('play/SET_PLAYING_SECTION', {
        ...this.playingSection,
        hls: videoUrl,
        customHost: ''
      })
      this.$store.commit('play/setVideoProgress', {
        ...this.currentVideoProgress,
        s: this.playNowTime
      })
    },
    // 切换到腾讯云线路
    async toggleTcLine(index) {
      let params = {
        video_id: this.playingSection.video_id,
        course_id: this.playingSection.course_id,
        line_id: +index + 1,
        course_section_id: this.playingSection.course_section_id
      }
      var timeStamp = Date.now()
      let options = {
        headers: {
          'App-Video-Timestamp': timeStamp,
          'App-Video-Sign': window.md5(
            timeStamp + params.course_id + params.video_id
          )
        }
      }
      let { errcode, errmsg, data } = await getTcVideo(params, options).catch(
        err => {
          return { errcode: 404, errmsg: '网络错误,请重试', data: {} }
        }
      )
      if (errcode !== 0)
        return this.$message({ type: 'error', message: errmsg })
      this.$store.commit('play/SET_PLAYING_SECTION', {
        ...this.playingSection,
        hls: window.atob(window.atob(data.token)),
        customHost: ''
      })
      this.$store.commit('play/setVideoProgress', {
        vid: this.playingSection.video_id,
        s: this.playNowTime
      })
    },
    // ali云加密
    async toggleAliAes() {
      this.$message({ type: 'info', message: '敬请期待' })
      // let params = {
      //         video_id: this.playingSection.video_id,
      //         course_id: this.playingSection.course_id,
      //         line_id: index+1,
      //         course_section_id: this.playingSection.course_section_id
      //       }
      //       var timeStamp = Date.now()
      //       let options = {
      //         headers: {
      //           'App-Video-Timestamp': timeStamp,
      //           'App-Video-Sign': window.md5(
      //             timeStamp + params.course_id + params.video_id
      //           )
      //         }
      //       }
      // let {errcode,errmsg,data} = await getAwsVideo(params,options).catch(err=>{
      //   return {errcode:404,errmsg:'网络错误,请重试',data:{}}
      // })
      // if(errcode!==0) return this.$message({type:'error',message:errmsg})
      //  this.playingSection.hls = this.decrypt(window.atob(window.atob(data.token)))
      // this.$store.commit('play/setVideoProgress',{...this.currentVideoProgress,s:this.playNowTime})
    },
    // 腾讯加密线路切换
    async toggleTcEncryptLine(data) {
      this.$store.commit('play/SET_PLAYING_SECTION', {
        ...this.playingSection,
        customHost: data.customHost,
        p_sign: data.p_sign
      })
    },
    // 切换线路
    toggleLine() {
      // 切换到下一个线路
      this.isShowErrorDialog = false
      let domains = JSON.parse(JSON.stringify(this.playingSection.domains))
      domains.unshift({ t: 0 })
      this.lineIndex =
        this.lineIndex === domains.length - 1 ? 0 : this.lineIndex + 1
      window.videoRuntime.lineIndex = this.lineIndex
      let lineData = domains[this.lineIndex]
      this.changeLine(lineData, this.lineIndex)
    },
    /**线路切换over */
    // 监听视频因网络问题加载缓慢出现的卡顿情况
    handleNetSlow() {
      clearInterval(this.netSlowTimer)
      this.netSlowTimer = setInterval(() => {
        this.currentNewTime = this.player.currentTime()
        if (this.currentNewTime === this.currentOldTime && !this.isPaused) {
          this.isShowLoding = true
        } else {
          this.currentOldTime = this.currentNewTime
          this.isShowLoding = false
        }
      }, 300)
    },
    // 设置进度条打点
    setProgressMark() {
      let duration = this.player.duration()
      let { points_exam_site = [] } = this.playingSection
      if (!(Array.isArray(points_exam_site) && points_exam_site.length)) return
      const progressMarkers = []
      points_exam_site.forEach((item, index) => {
        progressMarkers.push({
          second: item.second,
          offset:
            (item.second / duration) * 100 < 100
              ? (item.second / duration) * 100
              : 100,
          text: item.exam_site_desc
        })
      })
      // 进度条dom
      const progressBoxDom = document.querySelector('.vjs-progress-holder')
      // const progressInsertBeforeDom = document.querySelector('.vjs-play-progress')
      // 如果没有拿到进度条dom300ms后递归调用本方法
      if (!progressBoxDom) {
        setTimeout(() => {
          this.setProgressMark()
        }, 300)
        return
      }
      // 存放打点dom盒子
      const progressMarkDom = document.createElement('div')
      progressMarkDom.classList.add('progress-mark-box')
      progressMarkers.forEach(item => {
        // 打点dom
        // <img class="inline-center progress-mark-item-play" src="//image.zlketang.com/public/news/common_study/player_small_play.png">
        const progressMarkItemDom = document.createElement('div')
        progressMarkItemDom.classList.add('progress-mark-item')
        progressMarkItemDom.style.left = item.offset + '%'
        const dataStr = JSON.stringify(item)
        progressMarkItemDom.innerHTML = `<div class="${
          item.offset < 60
            ? 'progress-mark-item-text-left'
            : 'progress-mark-item-text-right'
        }" data=${dataStr}><div class="inline-center">${item.text}</div></div>`
        progressMarkItemDom.setAttribute('data', JSON.stringify(item))
        progressMarkDom.appendChild(progressMarkItemDom)
      })
      progressMarkDom.addEventListener('click', e => {
        e.stopPropagation()
        const eventDom = e.target
        const data = JSON.parse(eventDom.getAttribute('data'))
      })
      progressBoxDom.appendChild(progressMarkDom)
    },
    //知识点切换
    setSiteBtn() {
      let { points_exam_site = [] } = this.playingSection
      if (!(Array.isArray(points_exam_site) && points_exam_site.length)) return
      let el = document.querySelector('.site-change-btn')
      if (el) return
      // 创建样式元素
      let siteBox = document.createElement('div')
      siteBox.innerHTML = '知识点切换'
      siteBox.classList.add('site-change-btn', 'flex-center', 'mr14')
      siteBox.addEventListener('click', () => {
        let flag = this.player.isFullscreen()
        if (flag) {
          this.player.exitFullscreen()
        }
        this.$emit('open-site-teacher', this.isPaused)
        this.pauseVideo()
      })
      let panelBox = document.querySelector('.vjs-playback-rate')
      let controlBox = document.querySelector('.vjs-control-bar')
      controlBox.insertBefore(siteBox, panelBox)
    },
    // 截图
    screenShot() {
      let el = document.querySelector('.screen-shot-line-btn')
      if (el) return
      // 创建样式元素
      let lineBox = document.createElement('div')
      lineBox.innerHTML = '截图'
      lineBox.classList.add(
        'screen-shot-line-btn',
        'flex-center',
        'mr30',
        'ml12'
      )
      let rateBox = document.querySelector('.vjs-playback-rate')
      let controlBox = document.querySelector('.vjs-control-bar')
      controlBox.insertBefore(lineBox, rateBox)
      // 注入点击事件
      this.clickOpenScreenShot()
    },
    clickOpenScreenShot() {
      let el = document.querySelector('.screen-shot-line-btn')
      el.addEventListener('click', () => {
        let flag = this.player.isFullscreen()
        if (flag) {
          // 如果全屏再创建一个截图弹窗注入video
          setTimeout(() => {
            let el2 = document.querySelector('#screen-shot-box')
            let controlBox = document.querySelector('.tcplayer')
            el2.classList.add('screen-shot-box-full-screen')
            controlBox.appendChild(el2)
            // this.$parent.showScreenShot = false
          }, 10)
        }
        this.$parent.shotTheVideo()
      })
    },
    // 课程选择
    openSubjectChoseDialog() {
      let el = document.querySelector('.subject-line-btn')
      if (el) return
      // 创建样式元素
      let lineBox = document.createElement('div')
      lineBox.innerHTML = '课程选择'
      lineBox.classList.add('subject-line-btn', 'flex-center', 'mr30')
      let rateBox = document.querySelector('.vjs-playback-rate')
      let controlBox = document.querySelector('.vjs-control-bar')
      controlBox.insertBefore(lineBox, rateBox)
      // 注入点击事件
      this.clickChoseSubject()
    },
    clickChoseSubject() {
      let el = document.querySelector('.subject-line-btn')
      el.addEventListener('click', () => {
        this.$parent.choseSubject()
        this.$store.dispatch('play/toggleChooseClassDialog', true)
        this.pauseVideo()
        let flag = this.player.isFullscreen()
        if (flag) {
          // 如果全屏再创建一个截图弹窗注入video
          setTimeout(() => {
            let el2 = document.querySelector('.class-dialog-select')
            let controlBox = document.querySelector('.tcplayer')
            // el2.classList.add('screen-shot-box-full-screen')
            controlBox.appendChild(el2)
          }, 10)
        }
      })
    },
    // 注入上一节按钮 只是录播切换
    preSection() {
      // 1、第一个且是最后一个的话 上一个、下一个都不展示
      if (this.recordedBroadcastList.length <= 1) return
      // 2、是第一个的话只展示下一个
      this.hasPreSection = this.recordedBroadcastList.some(
        (item, index) =>
          +item.video_id === +this.playingSection.video_id && index === 0
      )
      if (this.hasPreSection) return
      let el = document.querySelector('.pre-section')
      if (el) return
      // 创建样式元素
      // 上一个
      let div = document.createElement('div')
      div.classList.add('flex-center')
      let preSection = document.createElement('img')
      preSection.src =
        '//image.zlketang.com/public/news/images/new_wap/pre_se.png'
      preSection.classList.add('pre-section', 'flex-center')
      div.appendChild(preSection)
      // 注入
      let playControlBox = document.querySelector('.vjs-volume-panel')
      let controlBox = document.querySelector('.vjs-control-bar')
      controlBox.insertBefore(div, playControlBox)
      // 绑定上一节点击事件
      this.clickPreSection()
    },
    // 注入下一节按钮
    nextSection() {
      // 1、第一个且是最后一个的话 上一个、下一个都不展示
      if (this.recordedBroadcastList.length <= 1) return
      // 3、是最后一个的话只展示上一个
      const hasDesiredVideo = this.recordedBroadcastList.some(
        (item, index) =>
          +item.video_id === +this.playingSection.video_id &&
          index === this.recordedBroadcastList.length - 1
      )
      if (hasDesiredVideo) return

      let el = document.querySelector('.next-section')
      if (el) return
      // 创建样式元素
      // 上一个
      let div = document.createElement('div')
      div.classList.add('flex-center')
      let preSection = document.createElement('img')
      preSection.src =
        '//image.zlketang.com/public/news/images/new_wap/next_se.png'
      this.hasPreSection
        ? preSection.classList.add('next-section', 'flex-center')
        : preSection.classList.add('next-section', 'flex-center', 'ml17')
      div.appendChild(preSection)
      // 注入
      let playControlBox = document.querySelector('.vjs-volume-panel')
      let controlBox = document.querySelector('.vjs-control-bar')
      controlBox.insertBefore(div, playControlBox)
      // 绑定下一节点击事件
      this.clickNextSection()
    },
    // 上一节点击事件
    clickPreSection() {
      let el = document.querySelector('.pre-section')
      el.addEventListener('click', () => {
        this.goToPreSection()
      })
    },
    // 切换上一节
    goToPreSection() {
      // 先判断是否要展开上一节的目录
      let flag =
        this.recordedBroadcastList.findIndex(
          item => +item.video_id === +this.playingSection.video_id
        ) - 1
      let obj = this.recordedBroadcastList[flag]
      // 找到要跳转的视频的最顶层 和 次顶层 展开
      let dir = this.findTree(this.courseMkInfo.options, obj)
      // 触发展开 floor 1展开第一级 2展开第二级 以及第一级
      if (+dir.length === 1) {
        this.$bus.$emit('openDir1', { dir: dir[0] })
      } else {
        this.$bus.$emit('openDir1', { dir: dir[0] })
        this.$bus.$emit('openDir2', { dir: dir[1] })
      }
      this.$nextTick(() => {
        // 执行点击逻辑
        let videoID = obj.video_id
        this.$bus.$emit('goToPreSection', { videoID })
      })
    },
    // 下一节点击事件
    clickNextSection() {
      let el = document.querySelector('.next-section')
      el.addEventListener('click', () => {
        this.goToNextSection()
      })
    },
    // 切换下一节
    goToNextSection() {
      let flag =
        this.recordedBroadcastList.findIndex(
          item => +item.video_id === +this.playingSection.video_id
        ) + 1
      let obj = this.recordedBroadcastList[flag]
      // 找到要跳转的视频的最顶层 和 次顶层 展开
      let dir = this.findTree(this.courseMkInfo.options, obj)
      // 触发展开 floor 1展开第一级 2展开第二级 以及第一级
      if (+dir.length === 1) {
        this.$bus.$emit('openDir1', { dir: dir[0] })
      } else {
        this.$bus.$emit('openDir1', { dir: dir[0] })
        this.$nextTick(() => {
          this.$bus.$emit('openDir2', { dir: dir[1] })
        })
      }
      setTimeout(() => {
        let videoID = obj.video_id
        this.$bus.$emit('goToPreSection', { videoID })
      }, 200)
    },
    // 将能播放的放到同一个数组里面
    getAllRecordedBroadcast() {
      this.courseMkInfo.options.map(item => {
        this.processItem(item)
      })
    },
    processItem(item) {
      if (+item.item_type === 1 && !item.placeholder_text) {
        this.recordedBroadcastList.push(item)
      }
      if (item.dir_id && item.children) {
        item.children.forEach(item2 => {
          this.processItem(item2)
        })
      }
    },
    //在一群树结构中 寻找指定子节点对应的树
    findTree(fatherNode, childNode) {
      let arr = []
      fatherNode.map((item, index) => {
        if (+item.video_id === +childNode.video_id) {
          arr.push(item)
        }
        if (item.children) {
          item.children.map((item2, index2) => {
            if (+item2.video_id === +childNode.video_id) {
              arr.push(item)
              // obj.floor = 1
            }
            if (item2.children) {
              item2.children.map(item3 => {
                if (+item3.video_id === +childNode.video_id) {
                  arr.push(item)
                  arr.push(item2)
                  // obj.floor = 2
                }
              })
            }
          })
        }
      })
      return arr
    },

    //自定义倍速点击事件
    customRateClick() {
      let dom = document.querySelector(
        '.vjs-playback-rate.vjs-menu-button.vjs-control'
      )
      //点击倍速时，让页面焦点失焦，回到BODY上，使上下左右等键控制音量生效
      dom.addEventListener('click', this.pageBlur)
    },
    // 刷新
    relaodVideo() {
      this.isShowErrorDialog = false
      this.player.dispose()
      this.initPlayer()
    },
    // 对播放器的按钮进行排序
    sortVideoBtn() {
      // 调整音量按钮的位置
      let volumeEl = document.querySelector('.vjs-volume-panel')
      let screenShotEl = document.querySelector('.screen-shot-line-btn')
      let controlBox = document.querySelector('.vjs-control-bar')
      // 插入到截图之前
      controlBox.insertBefore(volumeEl, screenShotEl)
    },
    // 监听键盘按键
    fastForward() {
      //在各个专业下的小窗口免费视频，为了阻止空格和上下键的默认事件，防止视频暂停、开始，音量加减时，页面滚动
      document.addEventListener('keyup', e => {
        // 获取页面中获得焦点的元素，确保不是输入框获取的焦点；
        let activeElement = document.activeElement
        if (activeElement.tagName !== 'BODY') {
          return
        }
        let event = e || window.event
        let keyCode = event.keyCode
        if (
          keyCode === this.SPACE_KEY_CODE ||
          keyCode === this.TOP_KEY_CODE ||
          keyCode === this.BOTTOM_KEY_CODE
        ) {
          event.preventDefault()
        }
      })
      document.addEventListener('keydown', e => {
        // 获取页面中获得焦点的元素，确保不是输入框获取的焦点；
        let activeElement = document.activeElement
        if (activeElement.tagName !== 'BODY') {
          return
        }
        // 监听左右按钮 控制快进快退
        let event = e || window.event
        let keyCode = event.keyCode
        let videoDom = document.querySelector('video')
        // 按下左键，快退
        if (keyCode === this.LEFT_KEY_CODE) {
          videoDom.currentTime -= this.fastForwardTime
        }
        if (keyCode === this.RIGHT_KEY_CODE) {
          videoDom.currentTime += this.fastForwardTime
        }
        if (keyCode === this.TOP_KEY_CODE) {
          this.volume += this.volumeStep
          if (this.volume >= 100) return (this.volume = 100)
          videoDom.volume = (this.volume / 100).toFixed(2)
          // 展示音量
          if (window.volumeTimer) {
            clearTimeout(window.volumeTimer)
          }
          this.isShowVolume = TextTrackCueList
          window.volumeTimer = setTimeout(() => {
            this.isShowVolume = false
            clearTimeout(window.volumeTimer)
          }, 1000)
        }
        if (keyCode === this.BOTTOM_KEY_CODE) {
          this.volume -= this.volumeStep
          if (this.volume <= 0) return (this.volume = 0)
          videoDom.volume = (this.volume / 100).toFixed(2)
          // 展示音量
          if (window.volumeTimer) {
            clearTimeout(window.volumeTimer)
          }
          this.isShowVolume = true
          window.volumeTimer = setTimeout(() => {
            this.isShowVolume = false
            clearTimeout(window.volumeTimer)
          }, 1000)
        }
        // 暂停播放
        if (keyCode === this.SPACE_KEY_CODE) {
          if (videoDom.paused) {
            videoDom.play()
          } else {
            videoDom.pause()
          }
          event.preventDefault()
        }
      })
    },
    // 视频插入广告相关配置
    configureVideoInsertionAdvertisement(currentTime) {
      let arr = this.sectionAdvertsList
      if (Array.isArray(arr) && arr.length > 0) {
        arr.map(item => {
          if (
            +item.duration + 1 >= currentTime &&
            +item.duration <= currentTime
          ) {
            this.advertisingObj = item
            this.showAdvertsCountdown = 8
            this.isShowAdverts = true
            if (this.shakeAnimate) {
              this.openAdvertAnimate()
            }
          }
        })
      }
    },
    // 打开广告动画
    openAdvertAnimate() {
      this.shakeAnimate = false
      // 先將父盒子归位
      let el2 = document.querySelector('.adverts-container')
      let el3 = document.querySelector('.adverts-time-box')
      el2.style.transform = 'scale(1)'
      el2.style.top = 12 + 'px'
      el2.style.right = 12 + 'px'
      el3.style.display = ''
      // 出现动画
      let el = document.querySelector('.animate-box')
      let flag = 303
      let timer = setInterval(() => {
        if (flag <= 0) {
          flag = 0
          el.style.left = 0
          clearInterval(timer)
        } else {
          el.style.left = flag - 10 + 'px'
          flag -= 10
        }
      }, 1)
    },
    showCountDown() {
      this.showAdvertsCountdown--
      if (this.showAdvertsCountdown > 0) {
        setTimeout(() => {
          this.showCountDown()
        }, 1000)
      } else {
        this.closeAdversDialog()
      }
    },
    // 关闭广告和点击广告跳转的埋点
    sendData(time, str) {
      const {
        use_type,
        product_id,
        live_id,
        jump_url_map,
        name
      } = this.advertisingObj
      if (!name) return
      let typeMap = {
        1: product_id,
        2: live_id,
        3: jump_url_map
      }
      this.entryActivity({
        ads_name: str ? name + str : name,
        position_name: '课节广告',
        attach: {
          jump_content: typeMap[+use_type],
          jump_type: this.jumpTypeMap[+use_type],
          profession_id: this.courseInfo.profession_id,
          subject_id: this.courseInfo.subject_id,
          countdown: time
        }
      })
    },
    closeAdversDialog() {
      // 关闭埋点事件
      this.sendData(this.showAdvertsCountdown, '_关闭')
      // 先关闭动画再清除
      let el = document.querySelector('.adverts-container')
      let el2 = document.querySelector('.adverts-time-box')
      el2.style.display = 'none'
      let scaleVal = 1
      let top = 12
      let right = 12
      let tVal = 1
      let timer = setInterval(() => {
        // 动画第一阶段先缩小 12 - -22 12 -
        // 上下距离随着宽度缩小要增加
        if (scaleVal > 0.1) {
          scaleVal = scaleVal - 0.03
          el.style.transform = `scale(${scaleVal})`
        } else {
          top = top - 1
          right = right + 2
          // 平移到指定位置消失
          if (top >= -22) {
            el.style.top = top + 'px'
            el.style.right = right + 'px'
          } else {
            this.shakeAnimate = true
            clearInterval(timer)
            this.isShowAdverts = false
            this.showAdvertsCountdown = 0
            this.advertisingObj = {}
          }
        }
      }, 1)
    },
    // 全屏状态改变的时候 由于是v-show展示的 所以这个div一直有 只需要根据全屏状态决定这个div在哪个位置即可
    // 全屏放在.tcplayer下 非全屏就正常结构.adverts-container下
    insertAdvertsFullScreen() {
      // 获取当前全屏元素 isFullscreen()方法不能正确监听esc退出全屏的状态？所以用原生
      let fullscreenElement =
        document.fullscreenElement ||
        document.mozFullScreenElement ||
        document.webkitFullscreenElement

      if (fullscreenElement) {
        let node = document.querySelector('.adverts-container')
        node.style.right = 36 + 'px'
        node.style.top = 36 + 'px'
        let controlBox = document.querySelector('.tcplayer')
        if (node) {
          controlBox.appendChild(node)
        }
      } else {
        let node = document.querySelector('.adverts-container')
        node.style.right = 12 + 'px'
        node.style.top = 12 + 'px'
        let controlBox = document.querySelector('.player-new-box')
        if (node) {
          controlBox.appendChild(node)
        }
      }
    },
    // 点击广告跳转 1-商品 2-直播 3-跳转地址
    goToAdvert(val) {
      this.pauseVideo()
      this.sendData(this.showAdvertsCountdown)
      // 埋点
      const {
        use_type,
        product_id,
        live_id,
        jump_url_map,
        name
      } = this.advertisingObj
      switch (+use_type) {
        case 1:
          this.goTo(`/course_detail.html`, {
            query: {
              product_id
            }, // 页面参数
            // delay: 200, // 延迟执行跳转
            replace: true, // target为“_blank”时，replace不生效
            target: '_blank' // _blank：跳转新页面，此时delay：不生效
          })
          break
        case 2:
          this.goTo(`/vcourse/live.html`, {
            query: {
              live_id
            }, // 页面参数
            // delay: 200, // 延迟执行跳转
            replace: true, // target为“_blank”时，replace不生效
            target: '_blank' // _blank：跳转新页面，此时delay：不生效
          })
          break
        case 3:
          // if (!jump_url_map.pc_jump_url.includes('https://')) {
          //   jump_url_map.pc_jump_url = 'https://' + jump_url_map.pc_jump_url
          // }
          let str = jump_url_map.pc_jump_url.trim()
          this.goTo(str, {
            query: {},
            target: '_blank'
          })
          break
      }
    },
    // 之了数据点击
    sendZlaClick() {
      this.keyClick({
        ...this.zlaBtn
      })
    }
  },
  watch: {
    zlaBtn: {
      handler() {
        this.sendZlaClick()
      },
      deep: true
    },
    isShowAdverts: {
      handler(val, OldVal) {
        if (val) {
          this.showCountDown()
        }
      },
      deep: true,
      immediate: false
    }
  },
  beforeMount() {
    entryStore.loadModule(this.$store, ['user'])
  },
  mounted() {
    this.initial() // initPlayer方法中在使用videoRuntime全局变量，因此需要先调用
    this.preparePlayer()
    this.fastForward()
    this.isPlayerNew && this.getAllRecordedBroadcast()
    // 目前只有看课页和免费看课页的视频植入了广告
    if (this.hasAdverts) {
      this.sectionAdvertsList = this.playingSection.section_adverts
    }
  },
  destroyed() {
    if (this.retryTimer) {
      clearTimeout(this.retryTimer)
      this.retryTimer = null
    }
    //特别关键！！！ 必须销毁重建，下一次播放才正常。
    if (this.player) {
      try {
        this.player.dispose()
      } catch (e) {}
      this.player = false
    }
    document.removeEventListener('keyup', this.handleKeyup)
    document.removeEventListener('keydown', this.handleKeydown)
    clearInterval(this.netSlowTimer)
  }
}
</script>
<style>
/*水印样式    */
.video-container
  .tcp-dynamic-watermark-container
  .tcp-dynamic-watermark-content {
  /* css-ignore */
  font-size: 12px;
  opacity: 1;
}
.video-container .vjs-error-display {
  /* css-ignore */
  display: none !important;
}
.video-container .draw-fill {
  /* css-ignore */
  display: none;
}

.video-container {
  /* css-ignore */
  position: relative;
  width: 100%;
  height: 100%;
}
.video-container .playerCont-dimensions {
  /* css-ignore */
  width: 100% !important;
  height: 100% !important;
}
.video-container .tcp-loading-spinner {
  /* css-ignore */
  display: none !important;
}
.video-container .vjs-big-play-button {
  /* css-ignore */
  display: none !important;
}

.video-container .vjs-errors-display {
  /* css-ignore */
  display: none;
}
.video-container .tcplayer {
  /* css-ignore */
  width: 100%;
  height: 100%;
}
</style>
<style scoped>
.pause-bg {
  background-color: rgba(0, 0, 0, 0.1);
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 48px;
  z-index: 0;
}
.control-tips-box {
  position: absolute;
  bottom: 150px;
  left: 50px;
  transition: all 0.3s ease;
  line-height: 40px;
  padding: 0 16px;
  color: #fff;
  background: rgba(31, 31, 31, 0.9);
  border-radius: 8px;
}
.watch-tips-box {
  position: absolute;
  bottom: 80px;
  left: 50px;
  background: rgba(0, 0, 0, 0.4);
  border-radius: 2px;
  padding: 6px 16px;
  font-size: 14px;
  font-weight: 400;
  color: #ffffff;
  line-height: 20px;
}
.tips-a {
  color: #ff8409;
}
.loading-box {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
.loading {
  width: 76px;
  height: 65px;
  background: rgba(0, 0, 0, 0.7);
  border-radius: 12px;
  font-size: 12px;
  font-weight: 400;
  color: #ffffff;
  line-height: 17px;
}
.loading-gif {
  margin-bottom: 2px;
  background: url('//image.zlketang.com/public/news/images/new_official/play/play_loading_bg.png');
  background-size: contain;
  width: 30px;
  height: 30px;
}
.puase-img {
  width: 40px;
  height: 40px;
  position: absolute;
  bottom: 60px;
  left: 30px;
}

/deep/ .toggle-line-list {
  position: absolute;
  width: 200%;
  left: -50%;
  bottom: 3em;
  max-height: 15em;
  background-color: rgba(0, 0, 0, 0.7);
}
/deep/ .toggle-line-list li {
  color: #fff;
  list-style: none;
  margin: 0;
  padding: 0.2em 0;
  line-height: 1.4em;
  font-size: 1.2em;
  text-align: center;
  text-transform: lowercase;
}
/deep/ .toggle-line-list li:hover {
  background-color: rgba(115, 133, 159, 0.5);
}
/deep/ .toggle-line-list .active-line {
  background-color: #fff !important;
  color: #2b333f !important;
}
.error-dialog {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: #000;
  font-size: 16px;
  font-weight: 400;
  color: #e5e5e5;
  line-height: 22px;
  padding: 20px;
  z-index: 9999;
}
.error-btn {
  color: #e6e6e6;
  text-align: center;
  width: 156px;
  height: 44px;
  background: #2e2e2e;
  border-radius: 8px;
  cursor: pointer;
}
/* 暂停 */
/deep/ .vjs-play-control .vjs-icon-placeholder:before,
.vjs-icon-play:before {
  content: '';
  display: block;
  width: 24px;
  height: 24px;
  background: url('//image.zlketang.com/public/news/images/douxue_wxpub_img/play_btn.png')
    no-repeat;
  background-size: 100% 100%;
  position: absolute;
  left: 30px;
  top: 7px;
}
/* 播放中 */
/deep/ .vjs-play-control.vjs-playing .vjs-icon-placeholder:before,
.vjs-icon-pause:before {
  content: '';
  display: block;
  width: 24px;
  height: 24px;
  background: url('//image.zlketang.com/public/news/images/douxue_wxpub_img/suspend.png')
    no-repeat;
  background-size: 100% 100%;
  position: absolute;
  left: 30px;
  top: 7px;
}

/* 播放按钮 */
/deep/ .vjs-play-control {
  width: 78px !important;
  position: relative;
}
/deep/ .vjs-control-bar {
  height: 40px !important;
  font-size: 12px;
  color: #ffffff;
}
/deep/ .vjs-button > .vjs-icon-placeholder:before {
  line-height: 40px;
}
/deep/ .vjs-time-control {
  line-height: 40px;
}
/deep/ .vjs-playback-rate-value {
  line-height: 40px !important;
  font-size: 12px !important;
}
/deep/ .video-js {
  /* line-height: 40px !important; */
}
/deep/ .vjs-playback-rate {
  width: 30px !important;
  /* margin-right: 30px; */
}
/deep/ .vjs-fullscreen-control {
  margin-right: 40px !important;
}
/deep/ .vjs-fullscreen-control .vjs-icon-placeholder:before,
.vjs-icon-fullscreen-enter:before {
  content: '\f108';
  font-size: 30px;
}
/* /deep/.video-js .vjs-subtitle .vjs-icon-placeholder:before {
  content: '\f108';
  font-size: 30px;
} */

/deep/ .vjs-playback-rate {
  margin-right: 10px;
}
/deep/ .vjs-button.vjs-control {
  width: auto;
  margin-right: 28px;
  cursor: pointer;
}
/deep/ .vjs-button.vjs-control:nth-last-child(2) {
  margin-right: 23px;
}
.vjs-custom-button {
  padding: 0 10px;
  cursor: pointer;
}
.vjs-custom-box {
  position: relative;
}
/deep/ .subtitle-size-box {
  margin-top: 10px;
}
/deep/ .vjs-control:hover .vjs-custom-content {
  display: block;
}
/deep/ .vjs-custom-content {
  display: none;
}
/deep/ .subtitle-setting {
  width: 220px;
  position: absolute;
  bottom: 40px;
  right: -10px;
  padding: 20px 30px;
  border-radius: 10px;
  background-color: rgba(0, 0, 0, 0.8);
  text-align: left;
  font-size: 14px;
}
/deep/ .subtitle-size-wrap {
  margin-top: 20px;
}
/deep/ .subtitle-size-t {
  margin-bottom: 10px;
}
/deep/ .subtitle-checkbox {
  position: relative;
  margin-left: 20px;
  box-sizing: content-box;
  /* 指定大小和圆角 */
  width: 2.4rem;
  height: 1.2rem;
  border: 1px solid #ccc;
  outline: 0;
  border-radius: 0.75rem;
  /* 默认的背景色是半透明黑色 */
  background-color: rgba(0, 0, 0, 0.1);
  /* 因为背景颜色和border在选中后发生了改变，所以就在这里加上过渡 */
  transition: background-color 0.3s, border 0.3s;
  /* 去掉checkbox的默认样式 */
  -webkit-appearance: none;
  /* 去掉checkbox默认的点击效果 */
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  cursor: pointer;
}

/deep/ .subtitle-checkbox::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  /* 长宽与父级的宽相同，并指定圆角*/
  width: 1.2rem;
  height: 1.2rem;
  border-radius: 0.7rem;
  background-color: #fff;
  /* 阴影是用来划分边界的 */
  box-shadow: 0 0 2px #999;
  /* 为了让开关有过渡效果，要使用transition属性。这个属性性能消耗大，要避免大规模使用 */
  transition: left 0.2s ease-in-out;
  cursor: pointer;
}
/* 选中状态下的样式 */
/deep/ .subtitle-checkbox:checked {
  border-color: #409eff;
  background-color: #409eff;
}

/* 选中状态下，圆点的左边缘位置等于父级宽度减去自己的宽度，这样就靠右了 */
/deep/ .subtitle-checkbox:checked:after {
  left: 1.2rem;
}
/deep/ .subtitle-size {
  padding: 2px 6px;
  cursor: pointer;
}
/deep/ .subtitle-size-active {
  color: #409eff;
}
.adverts-container {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 303px;
  height: 266px;
  z-index: 10;
  transform-origin: 0 0;
}
.adverts-time-box {
  width: 128px;
  height: 32px;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 16px;
  margin-left: 175px;

  font-size: 12px;
  color: #d9d9d9;
  line-height: 32px;
}
.adverts-card {
  width: 303px;
  height: 226px;
  background: rgba(0, 0, 0, 0.7);
  border-radius: 12px;
  margin-top: 8px;
  position: relative;
  cursor: pointer;
}
.adverts-time-box-line {
  width: 12px;
  height: 17px;
  font-size: 11px;
  color: #8c8c8c;
  line-height: 17px;
  margin-left: 4px;
  margin-right: 2px;
  margin-top: -2px;
  text-align: center;
}
.adverts-card-img {
  display: block;
  width: 295px;
  height: 164px;
  margin: 0 auto;
  border-radius: 8px;
}
.pt6 {
  padding-top: 6px;
}
.adverts-card-font {
  width: 197px;
  height: 17px;
  font-size: 12px;
  color: #ffffff;
  line-height: 17px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: 20px;
  margin-left: 12px;
  float: left;
}
.adverts-card-btn {
  position: absolute;
  right: 12px;
  bottom: 12px;
  min-width: 71px;
  padding-left: 12px;
  padding-right: 23px;
  height: 32px;
  background: #ff7038;
  border-radius: 6px;
  cursor: pointer;

  line-height: 31px;
  font-size: 12px;
  font-weight: 500;
  color: #ffffff;
}
.adverts-card-btn-icon {
  display: block;
  position: absolute;
  right: 9px;
  bottom: 11px;
  width: 10px;
  height: 10px;
  line-height: 10px;
  font-size: 12px;
}

/deep/ .backward-box-img {
  cursor: pointer;
  width: 20px;
  height: 20px;
  margin-left: 32px;
  margin-top: 10px;
}
/deep/ .forward-box-img {
  cursor: pointer;
  width: 20px;
  height: 20px;
  margin-left: 24px;
  margin-top: 10px;
}

/deep/ .vjs-sys {
  height: 40px;
  line-height: 40px;
  padding-right: 20px;
  margin-left: 10px;
  position: relative;
  cursor: pointer;
}
/deep/ .vjs-sys:hover .vjs-sys-hover-box {
  display: block;
}
/deep/ .vjs-sys-hover-box {
  display: none;
  position: absolute;
  width: 254px;
  height: 56px;
  background-color: rgba(0, 0, 0, 0.8);
  color: #fff;
  top: -56px;
  left: -27px;
  border-radius: 16px;
  padding: 20px;
  z-index: 100;
  cursor: context-menu;
}
/deep/ .vjs-sys-hover-box::after {
  position: absolute;
  bottom: -5px;
  left: 34px;
  transform: rotate(45deg);
  content: '';
  display: block;
  width: 0;
  height: 0;
  border-style: solid;
  border-color: transparent;
  /* 直角三角形 */
  border-width: 0px 0px 10px 10px;
  border-bottom-color: rgba(0, 0, 0, 0.5);
}
/deep/ .vjs-sys-label {
  height: 20px;
  font-size: 14px;
  font-weight: 400;
  color: #e6e6e6;
  line-height: 20px;
  margin-right: 10px;
  cursor: ;
}
/deep/ .vjs-sys-span {
  cursor: pointer;
  height: 20px;
  font-size: 14px;
  font-weight: 400;
  color: #e6e6e6;
  line-height: 20px;
  transition: all 0.3s ease;
  cursor: pointer;
}
/deep/ .vjs-switch {
  cursor: pointer;
  width: 38px;
  height: 20px;
  background: #8c8c8c;
  border-radius: 11px;
  padding: 1px;
  transition: all 0.3s ease;
}
/deep/ .vjs-switch-dor {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #ffffff;
  transition: all 0.3s ease;
}
/deep/ .vjs-sys-span-active {
  color: #5c9dff;
}
/deep/ .vjs-switch-active {
  background-color: #3480ff;
}
/deep/ .vjs-switch-active .vjs-switch-dor {
  transform: translateX(18px);
}
/deep/ .vjs-ended .vjs-icon-placeholder:before {
  content: '' !important;
}
/* 去掉字幕、设置的文字阴影，即闪光效果 */
/deep/ .video-js .vjs-control:focus,
/deep/ .video-js .vjs-control:focus:before,
/deep/ .video-js .vjs-control:hover:before {
  text-shadow: none !important;
}
/deep/ .vjs-playback-rate .vjs-menu {
  left: -6px;
}
/deep/ .vjs-menu li {
  font-size: 14px;
}
/deep/ .vjs-volume-control {
  left: -63px !important;
}
</style>
